---
title: Rendering PDF Pages to Canvas
description: Learn how to render PDF pages to an HTML canvas element using PDFium
searchable: true
---

# Rendering PDF Pages to Canvas

## Description

One of the most common operations when working with PDFs is rendering pages to display them visually. PDFium provides powerful page rendering capabilities through its WebAssembly API. This guide explains how to render PDF pages to an HTML canvas element with complete control over scaling, rotation, and other rendering parameters.

## Interactive Example

This interactive example demonstrates how to render PDF pages to a canvas element. You can navigate between pages, zoom in/out, and rotate the page:

import RenderPageToCanvasDemo from '../code-examples/render-page-to-canvas.preview';

<RenderPageToCanvasDemo />

## The Basic Workflow

Rendering a PDF page to a canvas involves several steps:

1. Initialize PDFium
2. Load the PDF document
3. Load the specific page to render
4. Create a bitmap for rendering
5. Render the page to the bitmap
6. Transfer the bitmap data to the canvas
7. Clean up resources

## Prerequisites

The examples in this guide use the `initializePdfium` helper function from our [Getting Started](/docs/pdfium/getting-started#initializing-pdfium) guide. This function properly initializes the PDFium library, including calling the required `PDFiumExt_Init()` function. Make sure to include this initialization code in your application before trying the examples below.

## Example Implementation

Here's a complete implementation for loading a PDF document and rendering its pages to a canvas:

```typescript
import { initializePdfium } from './initialize-pdfium';
// Note: The initializePdfium function is a helper that initializes the PDFium library.
// For the full implementation, see: /docs/pdfium/getting-started

/**
 * Loads a PDF document and returns an object with methods to access and render it.
 * This avoids loading the document multiple times for different operations.
 */
export async function loadPdfDocument(pdfData: Uint8Array) {
  // Initialize PDFium
  const pdfium = await initializePdfium();
  
  // Allocate memory for the PDF data
  const filePtr = pdfium.pdfium.wasmExports.malloc(pdfData.length);
  pdfium.pdfium.HEAPU8.set(pdfData, filePtr);
  
  // Load the document
  const docPtr = pdfium.FPDF_LoadMemDocument(filePtr, pdfData.length, 0);
  
  if (!docPtr) {
    const error = pdfium.FPDF_GetLastError();
    pdfium.pdfium.wasmExports.free(filePtr);
    
    // Handle password-protected documents
    if (error === 4) {
      return {
        hasPassword: true,
        pageCount: 0,
        close: () => {}, // No-op since no document was loaded
        getPageCount: () => 0,
        renderPage: async () => { throw new Error('Document is password protected'); }
      };
    }
    
    throw new Error(`Failed to load PDF: ${error}`);
  }
  
  // Get page count
  const pageCount = pdfium.FPDF_GetPageCount(docPtr);
  
  // Return an object with document info and rendering capabilities
  return {
    hasPassword: false,
    pageCount,
    
    // Close the document and free resources
    close: () => {
      pdfium.FPDF_CloseDocument(docPtr);
      pdfium.pdfium.wasmExports.free(filePtr);
    },
    
    // Get the current page count (useful if pages are added/removed)
    getPageCount: () => pdfium.FPDF_GetPageCount(docPtr),
    
    // Render a specific page to a canvas
    renderPage: async (
      pageIndex: number,
      scale: number = 1.0,
      rotation: number = 0,
      canvas: HTMLCanvasElement,
      dpr: number = window.devicePixelRatio || 1.0
    ): Promise<{
      width: number;
      height: number;
    }> => {
      // Check if the page index is valid
      if (pageIndex < 0 || pageIndex >= pageCount) {
        throw new Error(`Invalid page index: ${pageIndex}. Document has ${pageCount} pages.`);
      }
      
      // Load the page
      const pagePtr = pdfium.FPDF_LoadPage(docPtr, pageIndex);
      if (!pagePtr) {
        throw new Error(`Failed to load page ${pageIndex}`);
      }
      
      try {
        // Get the page dimensions
        const width = pdfium.FPDF_GetPageWidthF(pagePtr);
        const height = pdfium.FPDF_GetPageHeightF(pagePtr);
        
        // Calculate the scaled dimensions with device pixel ratio
        const effectiveScale = scale * dpr;
        let scaledWidth = Math.floor(width * effectiveScale);
        let scaledHeight = Math.floor(height * effectiveScale);
        
        // Apply rotation if requested
        let rotateFlag = 0;
        switch (rotation) {
          case 90: rotateFlag = 1; break;
          case 180: rotateFlag = 2; break;
          case 270: rotateFlag = 3; break;
        }
        
        // Swap dimensions for 90 and 270 degree rotations
        if (rotation === 90 || rotation === 270) {
          [scaledWidth, scaledHeight] = [scaledHeight, scaledWidth];
        }
        
        // Create a bitmap for rendering
        const bitmapPtr = pdfium.FPDFBitmap_Create(scaledWidth, scaledHeight, 0);
        if (!bitmapPtr) {
          throw new Error('Failed to create bitmap');
        }
        
        try {
          // Set canvas CSS dimensions for proper display
          canvas.style.width = `${scaledWidth / dpr}px`;
          canvas.style.height = `${scaledHeight / dpr}px`;
          
          // Set actual canvas buffer size
          canvas.width = scaledWidth;
          canvas.height = scaledHeight;
          
          // Fill the bitmap with white background
          pdfium.FPDFBitmap_FillRect(bitmapPtr, 0, 0, scaledWidth, scaledHeight, 0xFFFFFFFF);
          
          // Render the page to the bitmap
          pdfium.FPDF_RenderPageBitmap(
            bitmapPtr,
            pagePtr,
            0,
            0,
            scaledWidth,
            scaledHeight,
            rotateFlag,
            16  // Use FPDF_REVERSE_BYTE_ORDER flag for correct color representation
          );
          
          // Get the bitmap buffer
          const bufferPtr = pdfium.FPDFBitmap_GetBuffer(bitmapPtr);
          if (!bufferPtr) {
            throw new Error('Failed to get bitmap buffer');
          }
          
          const bufferSize = scaledWidth * scaledHeight * 4; // RGBA
          
          // Create a COPY of the buffer data to prevent memory issues
          // This is crucial - we must slice() to copy the data instead of using a view
          const buffer = new Uint8Array(
            pdfium.pdfium.HEAPU8.buffer, 
            pdfium.pdfium.HEAPU8.byteOffset + bufferPtr, 
            bufferSize
          ).slice();
          
          // Create ImageData from the buffer copy
          const imageData = new ImageData(
            new Uint8ClampedArray(buffer.buffer),
            scaledWidth,
            scaledHeight
          );
          
          // Draw the image data to the canvas
          const ctx = canvas.getContext('2d');
          if (!ctx) {
            throw new Error('Failed to get 2D context from canvas');
          }
          ctx.putImageData(imageData, 0, 0);
          
          // Return the dimensions adjusted for DPR
          return {
            width: scaledWidth / dpr,
            height: scaledHeight / dpr
          };
        } finally {
          // Clean up bitmap
          pdfium.FPDFBitmap_Destroy(bitmapPtr);
        }
      } finally {
        // Clean up page
        pdfium.FPDF_ClosePage(pagePtr);
      }
    }
  };
}
```

## Usage Example

Here's a simple example of how to use the rendering functions with a canvas element:

```javascript
// Get a reference to the canvas element
const canvas = document.getElementById('pdf-canvas');

// Load a PDF file
async function loadAndRenderPdf() {
  try {
    // Fetch the PDF file
    const response = await fetch('sample.pdf');
    const arrayBuffer = await response.arrayBuffer();
    const pdfData = new Uint8Array(arrayBuffer);
    
    // Load the document
    const pdfDocument = await loadPdfDocument(pdfData);
    
    // Set up page navigation
    let currentPage = 0;
    const pageCount = pdfDocument.pageCount;
    
    // Render the first page
    await pdfDocument.renderPage(currentPage, 1.0, 0, canvas);
    
    // Set up navigation
    document.getElementById('prev-button').addEventListener('click', async () => {
      if (currentPage > 0) {
        currentPage--;
        await pdfDocument.renderPage(currentPage, 1.0, 0, canvas);
      }
    });
    
    document.getElementById('next-button').addEventListener('click', async () => {
      if (currentPage < pageCount - 1) {
        currentPage++;
        await pdfDocument.renderPage(currentPage, 1.0, 0, canvas);
      }
    });
    
    // Clean up when done
    window.addEventListener('beforeunload', () => {
      pdfDocument.close();
    });
  } catch (error) {
    console.error('Error rendering PDF:', error);
  }
}

loadAndRenderPdf();
```

## Memory Management Considerations

When rendering PDF pages, proper memory management is critical to prevent memory leaks:

1. **Bitmap cleanup**: Always destroy bitmaps using `FPDFBitmap_Destroy` when you're done with them.

2. **Page cleanup**: Always close pages using `FPDF_ClosePage` when you're done with them.

3. **Buffer copying**: When accessing the bitmap buffer, create a copy of the data instead of using a direct view. This prevents issues with memory being freed while you're still using the data:

```typescript
// GOOD - Create a copy of the buffer data
const buffer = new Uint8Array(
  pdfium.pdfium.HEAPU8.buffer, 
  pdfium.pdfium.HEAPU8.byteOffset + bufferPtr, 
  bufferSize
).slice();

// BAD - Direct view can lead to problems if memory is freed
const buffer = new Uint8Array(
  pdfium.pdfium.HEAPU8.buffer, 
  pdfium.pdfium.HEAPU8.byteOffset + bufferPtr, 
  bufferSize
);
```

## Performance Tips

1. **Cache rendered pages**: If you're building a PDF viewer, consider caching rendered pages to avoid re-rendering the same page repeatedly.

2. **Progressive rendering**: For large documents, implement progressive loading and rendering to improve the perceived performance.

3. **Render at appropriate resolution**: Adjust the scale based on the display device. For high-DPI displays, you may want to render at a higher scale.

4. **Use Web Workers**: Consider using Web Workers for rendering to avoid blocking the main thread.

## Handling High-DPI Displays

The example implementation includes support for high-DPI displays (like Retina displays) through the optional `dpr` parameter, which defaults to `window.devicePixelRatio`. This ensures optimal rendering quality on all devices.

When working with high-DPI displays:

- The canvas is rendered at the physical resolution (scaled by DPR) for sharpness
- CSS dimensions are set to maintain the correct visual size
- The returned dimensions are adjusted to match the logical size rather than the buffer size

This approach prevents PDFs from appearing blurry on high-resolution displays while maintaining the expected size in the user interface.

```javascript
// Example of explicitly setting DPR when rendering
await pdfDocument.renderPage(
  0,              // First page
  1.0,            // 100% scale
  0,              // No rotation
  canvas,         // Canvas element
  window.devicePixelRatio  // Current device's pixel ratio
);
```

## Related Functions

- [FPDF_LoadMemDocument](/docs/pdfium/functions/FPDF_LoadMemDocument) - Load a PDF document from memory
- [FPDF_GetPageCount](/docs/pdfium/functions/FPDF_GetPageCount) - Get the number of pages in a document
- [FPDF_LoadPage](/docs/pdfium/functions/FPDF_LoadPage) - Load a specific page from a document
- FPDF_GetPageWidthF - Get the width of a page in points
- FPDF_GetPageHeightF - Get the height of a page in points
- FPDFBitmap_Create - Create a bitmap for rendering
- FPDFBitmap_FillRect - Fill a rectangle in a bitmap
- FPDF_RenderPageBitmap - Render a page to a bitmap
- FPDFBitmap_GetBuffer - Get the buffer containing bitmap data
- FPDFBitmap_Destroy - Destroy a bitmap and release resources
- [FPDF_ClosePage](/docs/pdfium/functions/FPDF_ClosePage) - Close a page and release resources
- [FPDF_CloseDocument](/docs/pdfium/functions/FPDF_CloseDocument) - Close a document and release resources
- [FPDF_GetLastError](/docs/pdfium/functions/FPDF_GetLastError) - Get the error code for the last failed operation

